package com.powerbank.user.service.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.powerbank.common.entity.*;
import com.powerbank.common.exception.BusinessException;
import com.powerbank.common.result.ResultCode;
import com.powerbank.user.dto.ScanRentalDTO;
import com.powerbank.user.dto.ScanReturnDTO;
import com.powerbank.user.mapper.*;
import com.powerbank.user.service.RentalService;
import com.powerbank.user.service.BillingService;
import com.powerbank.user.service.DeviceCommunicationService;
import com.powerbank.user.vo.RentalOrderVO;
import com.powerbank.user.vo.BillingInfoVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.Duration;

/**
 * 租借服务实现类
 */
@Slf4j
@Service
public class RentalServiceImpl implements RentalService {

    @Autowired
    private RentalOrderMapper rentalOrderMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private UserIdentityMapper userIdentityMapper;

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private DeviceLocationMapper deviceLocationMapper;

    @Autowired
    private BillingService billingService;

    @Autowired
    private DeviceCommunicationService deviceCommunicationService;

    // 默认押金金额
    private static final BigDecimal DEFAULT_DEPOSIT_AMOUNT = new BigDecimal("99.00");

    @Override
    @Transactional(rollbackFor = Exception.class)
    public RentalOrderVO scanRental(Long userId, ScanRentalDTO rentalDTO) {
        log.info("用户{}扫码租借，二维码：{}", userId, rentalDTO.getQrCode());

        // 1. 租借前置检查
        performPreRentalChecks(userId);

        // 2. 解析二维码获取设备信息
        String deviceCode = parseQrCode(rentalDTO.getQrCode());
        Device device = getDeviceByCode(deviceCode);

        // 3. 检查设备状态
        validateDeviceStatus(device);

        // 4. 创建租借订单
        RentalOrder order = createRentalOrder(userId, device, rentalDTO);
        rentalOrderMapper.insert(order);

        // 5. 更新设备插槽状态
        updateDeviceSlots(device);

        // 6. 构建返回结果
        return buildRentalOrderVO(order, device);
    }

    @Override
    public RentalOrderVO getCurrentRentalOrder(Long userId) {
        LambdaQueryWrapper<RentalOrder> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RentalOrder::getUserId, userId)
                .eq(RentalOrder::getOrderStatus, 1) // 租借中
                .orderByDesc(RentalOrder::getCreatedTime)
                .last("LIMIT 1");

        RentalOrder order = rentalOrderMapper.selectOne(wrapper);
        if (order == null) {
            return null;
        }

        Device device = deviceMapper.selectById(order.getDeviceId());
        return buildRentalOrderVO(order, device);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public RentalOrderVO confirmPayment(Long userId, Long orderId) {
        RentalOrder order = rentalOrderMapper.selectById(orderId);
        if (order == null || !order.getUserId().equals(userId)) {
            throw new BusinessException(ResultCode.ORDER_NOT_EXIST);
        }

        if (order.getOrderStatus() != 1) {
            throw new BusinessException(ResultCode.ORDER_STATUS_ERROR);
        }

        // 更新支付状态
        order.setPaymentStatus(1); // 已支付
        order.setUpdatedTime(LocalDateTime.now());
        rentalOrderMapper.updateById(order);

        // 发送开柜指令到设备
        Device device = deviceMapper.selectById(order.getDeviceId());
        boolean unlockSuccess = deviceCommunicationService.sendUnlockCommand(device.getDeviceCode(), order.getSlotNo());
        
        if (!unlockSuccess) {
            log.warn("设备{}插槽{}开锁失败", device.getDeviceCode(), order.getSlotNo());
            // TODO: 处理开锁失败的情况，可能需要退款或重试
        } else {
            log.info("设备{}插槽{}开锁成功", device.getDeviceCode(), order.getSlotNo());
        }

        return buildRentalOrderVO(order, device);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean cancelRental(Long userId, Long orderId) {
        RentalOrder order = rentalOrderMapper.selectById(orderId);
        if (order == null || !order.getUserId().equals(userId)) {
            throw new BusinessException(ResultCode.ORDER_NOT_EXIST);
        }

        if (order.getOrderStatus() != 1 || order.getPaymentStatus() == 1) {
            throw new BusinessException(ResultCode.ORDER_CANNOT_CANCEL);
        }

        // 更新订单状态为已取消
        order.setOrderStatus(5);
        order.setUpdatedTime(LocalDateTime.now());
        rentalOrderMapper.updateById(order);

        // 恢复设备插槽数量
        Device device = deviceMapper.selectById(order.getDeviceId());
        device.setAvailableSlots(device.getAvailableSlots() + 1);
        device.setOccupiedSlots(device.getOccupiedSlots() - 1);
        deviceMapper.updateById(device);

        return true;
    }

    /**
     * 租借前置检查
     */
    private void performPreRentalChecks(Long userId) {
        // 1. 验证用户是否存在
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new BusinessException(ResultCode.USER_NOT_EXIST);
        }

        // 2. 检查用户账户状态
        if (user.getStatus() != 1) {
            throw new BusinessException(ResultCode.USER_ACCOUNT_DISABLED);
        }

        // 3. 检查用户是否有未完成的订单
        LambdaQueryWrapper<RentalOrder> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RentalOrder::getUserId, userId)
                .eq(RentalOrder::getOrderStatus, 1); // 租借中
        
        long unfinishedCount = rentalOrderMapper.selectCount(wrapper);
        if (unfinishedCount > 0) {
            throw new BusinessException(ResultCode.USER_HAS_UNFINISHED_ORDER);
        }

        // 4. 检查实名认证状态（中级认证以上免押金）
        LambdaQueryWrapper<UserIdentity> identityWrapper = new LambdaQueryWrapper<>();
        identityWrapper.eq(UserIdentity::getUserId, userId);
        UserIdentity identity = userIdentityMapper.selectOne(identityWrapper);
        
        // 如果没有中级以上认证，需要检查余额/信用状态
        if (identity == null || identity.getVerifyStatus() != 1 || identity.getVerifyLevel() < 2) {
            // TODO: 检查用户余额是否足够支付押金
            log.info("用户{}需要支付押金", userId);
        }
    }

    /**
     * 解析二维码获取设备编码
     */
    private String parseQrCode(String qrCode) {
        // 假设二维码格式为：https://qr.powerbank.com/{deviceCode}
        if (StrUtil.isBlank(qrCode)) {
            throw new BusinessException(ResultCode.PARAM_ERROR.getCode(), "二维码内容不能为空");
        }

        // 简单解析，实际项目中可能需要更复杂的解析逻辑
        if (qrCode.contains("/")) {
            String[] parts = qrCode.split("/");
            return parts[parts.length - 1];
        }
        
        return qrCode;
    }

    /**
     * 根据设备编码获取设备信息
     */
    private Device getDeviceByCode(String deviceCode) {
        LambdaQueryWrapper<Device> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Device::getDeviceCode, deviceCode);
        Device device = deviceMapper.selectOne(wrapper);
        
        if (device == null) {
            throw new BusinessException(ResultCode.DEVICE_NOT_EXIST);
        }
        
        return device;
    }

    /**
     * 验证设备状态
     */
    private void validateDeviceStatus(Device device) {
        // 检查设备是否在线
        if (device.getOnlineStatus() != 1) {
            throw new BusinessException(ResultCode.DEVICE_OFFLINE);
        }

        // 检查设备状态
        if (device.getDeviceStatus() != 1) {
            if (device.getDeviceStatus() == 3) {
                throw new BusinessException(ResultCode.DEVICE_MAINTENANCE);
            } else {
                throw new BusinessException(ResultCode.PARAM_ERROR.getCode(), "设备当前不可用");
            }
        }

        // 检查是否有可用充电宝
        if (device.getAvailableSlots() <= 0) {
            throw new BusinessException(ResultCode.DEVICE_NO_POWERBANK);
        }
    }

    /**
     * 创建租借订单
     */
    private RentalOrder createRentalOrder(Long userId, Device device, ScanRentalDTO rentalDTO) {
        RentalOrder order = new RentalOrder();
        order.setOrderNo(generateOrderNo());
        order.setUserId(userId);
        order.setDeviceId(device.getId());
        order.setPowerbankId(1L); // TODO: 实际应该分配具体的充电宝
        order.setSlotNo(1); // TODO: 实际应该分配具体的插槽
        order.setRentalLocationId(device.getLocationId());
        order.setRentalTime(LocalDateTime.now());
        order.setDepositAmount(DEFAULT_DEPOSIT_AMOUNT);
        order.setOrderStatus(1); // 租借中
        order.setPaymentStatus(0); // 待支付
        order.setPaymentMethod("ALIPAY"); // 默认支付宝
        order.setCouponId(rentalDTO.getCouponId());
        order.setCreatedTime(LocalDateTime.now());
        order.setUpdatedTime(LocalDateTime.now());

        return order;
    }

    /**
     * 更新设备插槽状态
     */
    private void updateDeviceSlots(Device device) {
        device.setAvailableSlots(device.getAvailableSlots() - 1);
        device.setOccupiedSlots(device.getOccupiedSlots() + 1);
        device.setUpdatedTime(LocalDateTime.now());
        deviceMapper.updateById(device);
    }

    /**
     * 构建租借订单VO
     */
    private RentalOrderVO buildRentalOrderVO(RentalOrder order, Device device) {
        RentalOrderVO vo = new RentalOrderVO();
        BeanUtils.copyProperties(order, vo);

        // 设置设备信息
        vo.setDeviceCode(device.getDeviceCode());
        vo.setDeviceName(device.getDeviceName());

        // 获取点位信息
        DeviceLocation location = deviceLocationMapper.selectById(device.getLocationId());
        if (location != null) {
            vo.setLocationName(location.getLocationName());
            vo.setAddress(location.getAddress());
        }

        // 设置状态描述
        vo.setStatusDesc(getOrderStatusDesc(order.getOrderStatus()));

        // 设置支付链接（模拟）
        if (order.getPaymentStatus() == 0) {
            vo.setPaymentUrl("https://pay.powerbank.com/order/" + order.getOrderNo());
        }

        return vo;
    }

    /**
     * 获取订单状态描述
     */
    private String getOrderStatusDesc(Integer orderStatus) {
        switch (orderStatus) {
            case 1:
                return "租借中";
            case 2:
                return "已归还";
            case 3:
                return "超时未还";
            case 4:
                return "异常";
            case 5:
                return "已取消";
            default:
                return "未知状态";
        }
    }

    /**
     * 生成订单号
     */
    private String generateOrderNo() {
        return "R" + System.currentTimeMillis() + RandomUtil.randomLong(1000, 9999);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public RentalOrderVO scanReturn(Long userId, ScanReturnDTO returnDTO) {
        log.info("用户{}扫码归还，二维码：{}", userId, returnDTO.getQrCode());

        // 1. 获取用户当前租借订单
        RentalOrder currentOrder = getCurrentActiveOrder(userId);
        if (currentOrder == null) {
            throw new BusinessException(ResultCode.PARAM_ERROR.getCode(), "没有找到正在租借的订单");
        }

        // 2. 解析二维码获取归还设备信息
        String returnDeviceCode = parseQrCode(returnDTO.getQrCode());
        Device returnDevice = getDeviceByCode(returnDeviceCode);

        // 3. 验证归还设备状态
        validateReturnDeviceStatus(returnDevice);

        // 4. 更新订单归还信息
        updateOrderReturnInfo(currentOrder, returnDevice, returnDTO);

        // 5. 更新设备状态（租借设备和归还设备）
        updateDeviceStatusForReturn(currentOrder, returnDevice);

        // 6. 获取返回设备信息
        Device device = deviceMapper.selectById(currentOrder.getDeviceId());
        
        return buildRentalOrderVO(currentOrder, device);
    }

    @Override
    public BillingInfoVO getReturnBillingPreview(Long userId, Long orderId) {
        // 验证订单存在且属于当前用户
        RentalOrder order = rentalOrderMapper.selectById(orderId);
        if (order == null || !order.getUserId().equals(userId)) {
            throw new BusinessException(ResultCode.ORDER_NOT_EXIST);
        }

        if (order.getOrderStatus() != 1) {
            throw new BusinessException(ResultCode.ORDER_STATUS_ERROR.getCode(), "订单状态不正确，无法获取费用预览");
        }

        // 调用计费服务获取实时费用
        return billingService.calculateCurrentBilling(orderId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public RentalOrderVO confirmReturn(Long userId, Long orderId) {
        log.info("用户{}确认归还订单{}", userId, orderId);

        // 1. 验证订单存在且属于当前用户
        RentalOrder order = rentalOrderMapper.selectById(orderId);
        if (order == null || !order.getUserId().equals(userId)) {
            throw new BusinessException(ResultCode.ORDER_NOT_EXIST);
        }

        // 2. 检查订单状态（应该已经扫码归还但未结算）
        if (order.getOrderStatus() != 1 || order.getReturnTime() == null) {
            throw new BusinessException(ResultCode.ORDER_STATUS_ERROR.getCode(), "订单状态不正确，请先扫码归还");
        }

        // 3. 计算最终费用并更新计费记录
        BillingInfoVO finalBilling = billingService.updateBillingRecord(orderId);

        // 4. 更新订单状态为已归还
        order.setOrderStatus(2); // 已归还
        order.setTotalAmount(finalBilling.getCurrentFee());
        order.setActualAmount(finalBilling.getActualFee());
        order.setDiscountAmount(finalBilling.getCouponDiscount());
        order.setUpdatedTime(LocalDateTime.now());
        rentalOrderMapper.updateById(order);

        // 5. TODO: 调用支付服务处理费用结算（押金退还、费用扣取）
        processPaymentSettlement(order, finalBilling);

        // 6. 返回最终订单信息
        Device device = deviceMapper.selectById(order.getDeviceId());
        return buildRentalOrderVO(order, device);
    }

    /**
     * 获取用户当前活跃订单
     */
    private RentalOrder getCurrentActiveOrder(Long userId) {
        LambdaQueryWrapper<RentalOrder> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RentalOrder::getUserId, userId)
                .eq(RentalOrder::getOrderStatus, 1) // 租借中
                .eq(RentalOrder::getPaymentStatus, 1) // 已支付
                .orderByDesc(RentalOrder::getCreatedTime)
                .last("LIMIT 1");

        return rentalOrderMapper.selectOne(wrapper);
    }

    /**
     * 验证归还设备状态
     */
    private void validateReturnDeviceStatus(Device returnDevice) {
        // 检查设备是否在线
        if (returnDevice.getOnlineStatus() != 1) {
            throw new BusinessException(ResultCode.DEVICE_OFFLINE.getCode(), "归还设备当前离线，无法归还");
        }

        // 检查设备状态
        if (returnDevice.getDeviceStatus() != 1) {
            if (returnDevice.getDeviceStatus() == 3) {
                throw new BusinessException(ResultCode.DEVICE_MAINTENANCE.getCode(), "归还设备正在维护中，请更换其他设备");
            } else {
                throw new BusinessException(ResultCode.PARAM_ERROR.getCode(), "归还设备当前不可用");
            }
        }

        // 检查是否有空闲插槽
        if (returnDevice.getAvailableSlots() >= returnDevice.getTotalSlots()) {
            throw new BusinessException(ResultCode.DEVICE_FULL.getCode(), "归还设备已满，请更换其他设备");
        }
    }

    /**
     * 更新订单归还信息
     */
    private void updateOrderReturnInfo(RentalOrder order, Device returnDevice, ScanReturnDTO returnDTO) {
        LocalDateTime returnTime = LocalDateTime.now();
        
        order.setReturnTime(returnTime);
        order.setReturnLocationId(returnDevice.getLocationId());
        order.setReturnDeviceId(returnDevice.getId());
        order.setReturnSlotNo(returnDTO.getSlotNo() != null ? returnDTO.getSlotNo() : 1); // 默认插槽
        
        // 计算实际使用时长（分钟）
        long durationMinutes = Duration.between(order.getRentalTime(), returnTime).toMinutes();
        order.setActualDuration((int) Math.max(0, durationMinutes));
        
        order.setUpdatedTime(LocalDateTime.now());
        
        // 如果有备注，更新备注
        if (returnDTO.getRemark() != null) {
            order.setRemark(order.getRemark() != null ? 
                order.getRemark() + "; 归还备注: " + returnDTO.getRemark() : 
                "归还备注: " + returnDTO.getRemark());
        }
        
        rentalOrderMapper.updateById(order);
        
        log.info("订单{}归还信息已更新，实际使用时长: {}分钟", order.getId(), order.getActualDuration());
    }

    /**
     * 更新设备状态（租借和归还）
     */
    private void updateDeviceStatusForReturn(RentalOrder order, Device returnDevice) {
        // 1. 更新租借设备状态（释放插槽）
        Device rentalDevice = deviceMapper.selectById(order.getDeviceId());
        if (rentalDevice != null) {
            rentalDevice.setAvailableSlots(rentalDevice.getAvailableSlots() + 1);
            rentalDevice.setOccupiedSlots(rentalDevice.getOccupiedSlots() - 1);
            rentalDevice.setUpdatedTime(LocalDateTime.now());
            deviceMapper.updateById(rentalDevice);
            log.info("租借设备{}插槽已释放", rentalDevice.getDeviceCode());
        }

        // 2. 更新归还设备状态（占用插槽）
        if (!returnDevice.getId().equals(order.getDeviceId())) {
            returnDevice.setAvailableSlots(returnDevice.getAvailableSlots() - 1);
            returnDevice.setOccupiedSlots(returnDevice.getOccupiedSlots() + 1);
            returnDevice.setUpdatedTime(LocalDateTime.now());
            deviceMapper.updateById(returnDevice);
            log.info("归还设备{}插槽已占用", returnDevice.getDeviceCode());
        }

        // TODO: 发送设备指令（开启归还插槽、关闭租借插槽）
        // 1. 对归返设备发送开启指令（开启插槽用于接收充电宝）
        boolean openSlotSuccess = deviceCommunicationService.sendUnlockCommand(returnDevice.getDeviceCode(), order.getReturnSlotNo());
        if (openSlotSuccess) {
            log.info("归还设备{}插槽{}开启成功", returnDevice.getDeviceCode(), order.getReturnSlotNo());
        } else {
            log.warn("归还设备{}插槽{}开启失败", returnDevice.getDeviceCode(), order.getReturnSlotNo());
        }
        
        // 2. 如果是不同设备，对租借设备发送关闭指令（释放插槽）
        if (!returnDevice.getId().equals(order.getDeviceId())) {
            boolean closeSlotSuccess = deviceCommunicationService.sendLockCommand(rentalDevice.getDeviceCode(), order.getSlotNo());
            if (closeSlotSuccess) {
                log.info("租借设备{}插槽{}关闭成功", rentalDevice.getDeviceCode(), order.getSlotNo());
            } else {
                log.warn("租借设备{}插槽{}关闭失败", rentalDevice.getDeviceCode(), order.getSlotNo());
            }
        }
    }

    /**
     * 处理支付结算（押金退还、费用扣取）
     */
    private void processPaymentSettlement(RentalOrder order, BillingInfoVO billing) {
        log.info("处理订单{}支付结算，总费用: {}, 实际费用: {}", 
                order.getId(), billing.getCurrentFee(), billing.getActualFee());

        // TODO: 调用支付服务处理以下逻辑：
        // 1. 如果有费用，从押金中扣除
        // 2. 如果押金不足，从用户钱包扣除
        // 3. 退还剩余押金
        // 4. 更新支付状态
        
        BigDecimal refundAmount = order.getDepositAmount().subtract(billing.getActualFee());
        if (refundAmount.compareTo(BigDecimal.ZERO) > 0) {
            order.setRefundAmount(refundAmount);
            order.setPaymentStatus(2); // 部分退款
            log.info("退还押金: {}", refundAmount);
        } else {
            order.setRefundAmount(BigDecimal.ZERO);
            order.setPaymentStatus(1); // 已支付（无退款）
            log.info("押金全部用于支付费用，无需退还");
        }

        rentalOrderMapper.updateById(order);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean reportDeviceFault(Long userId, String deviceCode, String faultType, String faultDescription) {
        log.info("用户{}上报设备{}故障 - 类型: {}, 描述: {}", userId, deviceCode, faultType, faultDescription);

        // 1. 获取设备信息
        Device device = getDeviceByCode(deviceCode);
        if (device == null) {
            throw new BusinessException(ResultCode.DEVICE_NOT_EXIST);
        }

        // 2. 创建故障报告（这里简化处理，实际应该有专门的故障报告表）
        // 在实际应用中，应该创建故障报告记录并通知运维人员
        log.info("设备{}故障已记录 - 类型: {}, 描述: {}", deviceCode, faultType, faultDescription);

        // 3. 更新设备状态为故障
        device.setDeviceStatus(3); // 故障状态
        device.setUpdatedTime(LocalDateTime.now());
        deviceMapper.updateById(device);

        // 4. 如果用户有正在进行的订单，可能需要特殊处理
        LambdaQueryWrapper<RentalOrder> orderWrapper = new LambdaQueryWrapper<>();
        orderWrapper.eq(RentalOrder::getUserId, userId)
                .eq(RentalOrder::getDeviceId, device.getId())
                .eq(RentalOrder::getOrderStatus, 1); // 租借中

        RentalOrder order = rentalOrderMapper.selectOne(orderWrapper);
        if (order != null) {
            // 如果用户正在使用该设备，可能需要特殊处理
            log.warn("用户{}正在使用故障设备{}，订单ID: {}", userId, deviceCode, order.getId());
            // TODO: 通知客服处理特殊情况
        }

        return true;
    }
}